﻿using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;

namespace KuiHuaBaoDian.Services.Audio {

    internal sealed partial class KHBD_AudioService {

        private sealed class SFXRecord {

            private readonly AudioClip m_Clip;
            private readonly IAudioContainer m_Container;
            private readonly IAudioModifier m_Modifier;
            private readonly IAudioEvent m_Event;
            private readonly EventInstance m_EventInstance;

            private ObjectPool<AudioSource> m_SourcePool;

            private int m_LastPlayFrame;

            public SFXRecord(AudioClip clip, IAudioContainer container, EventInstance eventInstance, IAudioModifier modifier, ObjectPool<AudioSource> sourcePool) {
                UnityEngine.Assertions.Assert.IsNotNull(clip);
                UnityEngine.Assertions.Assert.IsNotNull(container);
                UnityEngine.Assertions.Assert.IsNotNull(eventInstance);
                UnityEngine.Assertions.Assert.IsNotNull(modifier);
                UnityEngine.Assertions.Assert.IsNotNull(sourcePool);

                m_SourcePool = sourcePool;

                m_Clip = clip;
                m_Container = container;
                m_EventInstance = eventInstance;
                m_Event = m_EventInstance.Event;
                m_Modifier = modifier;
            }

            private int m_PlayingCount;
            public int PlayingCount => m_Container.IsParticipateGlobalLimit ? m_PlayingCount : 0;

            private float GetVolume() {
                var volumeDB = m_Event.Modifier.Volume + m_Event.Mixer.FinalVolume + m_Container.Modifier.Volume;
                if (m_Container.IsParticipateGlobalLimit && m_Modifier != null) {
                    volumeDB += m_Modifier.Volume;
                }
                var volume = Mathf.InverseLerp(-60f, 0f, volumeDB);
                return Mathf.Clamp01(volume);
            }

            public void Play() {
                if (UnityEngine.Time.frameCount == m_LastPlayFrame) {
                    return;
                }
                m_LastPlayFrame = UnityEngine.Time.frameCount;

                var source = m_SourcePool.Get();
                source.clip = m_Clip;
                source.time = 0;
                source.volume = GetVolume();
                source.Play();
                m_PlayingCount++;

                UniTask.Void(async () => {
                    await KHBD.Context.WaitUntil(() => !source.isPlaying, CancellationToken.None);
                    m_PlayingCount--;
                    await UniTask.WaitForSeconds(1f);
                    m_SourcePool.Release(source);
                });
            }
        }
    }
}
